<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
        body {
  background: #222;
}

h2 {
  color: #666;
  font-family: monospace;
  text-align: center;
}

.background {
  table-layout: fixed;
  border-spacing: 0;
}

.background td {
  padding: 0;
}

.lava, .actor {
  background: #e55;
}

.wall {
  background: #444;
  border: solid 3px #333;
  box-sizing: content-box;
}

.actor {
  position: absolute;
}

.coin {
  background: #e2e838;
  border-radius: 50%;
}

.player {
  background: #335699;
  box-shadow: none;
}

.lost .player {
  background: #a04040;
}

.won .player {
  background: green;
}

.game {
  position: relative;
  overflow: hidden;
}
</style>
<body>

</body>
</html>
<script>
// 地图
var LEVELS = [
  ["                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                o                                                               ",
   "                                                                                ",
   "                      x ! x    x ! x   x ! x    x ! x    x ! x    x ! x         ",
   "                      x!!!x    x!!!x   x!!!x    x!!!x    x!!!x    x!!!x         ",
   "                      xx!xx    xx!xx   xx!xx    xx!xx    xx!xx    xx!xx         ",
   "                       xvx      xvx     xvx      xvx      xvx      xvx          ",
   "                x                                                           xx  ",
   "  xx            x                            o o                             x  ",
   "  x             x            o                                               x  ",
   "  x             x                                xxxxx                     o x  ",
   "  x          xxxx            o                                               x  ",
   "  x  @       x  x                                                xxxxx       x  ",
   "  xxxxxxxxxxxx  xxxxxxxxxxxxxxx   xxxxxxxxxxxxxxxxxxxx     xxxxxxx   xxxxxxxxx  ",
   "                              x   x                  x     x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              xxxxx                  xxxxxxx                    ",
   "                                                                                ",
   "                                                                                "],
  ["                                      x!!x                        xxxxxxx                                    x!x  ",
   "                                      x!!x                     xxxx     xxxx                                 x!x  ",
   "                                      x!!xxxxxxxxxx           xx           xx                                x!x  ",
   "                                      xx!!!!!!!!!!xx         xx             xx                               x!x  ",
   "                                       xxxxxxxxxx!!x         x                                    o   o   o  x!x  ",
   "                                                xx!x         x     o   o                                    xx!x  ",
   "                                                 x!x         x                                xxxxxxxxxxxxxxx!!x  ",
   "                                                 xvx         x     x   x                        !!!!!!!!!!!!!!xx  ",
   "                                                             xx  |   |   |  xx            xxxxxxxxxxxxxxxxxxxxx   ",
   "                                                              xx!!!!!!!!!!!xx            v                        ",
   "                                                                xxxx!!!!!xxxx                                      ",
   "                                               x     x            xxxxxxx        xxx         xxx                  ",
   "                                               x     x                           x x     v   x x                  ",
   "                                               x     x                             x         x                    ",
   "                                               x     x                             xx        x                    ",
   "                                               xx    x                             x         x                    ",
   "                                               x     x      o  o     x   x         x         x                    ",
   "               xxxxxxx        xxx   xxx        x     x               x   x         x         x                    ",
   "              xx     xx         x   x          x     x     xxxxxx    x   x   xxxxxxxxx       x                    ",
   "             xx       xx        x o x          x    xx               x   x   x               x                    ",
   "     @       x         x        x   x          x     x               x   x   x               x                    ",
   "    xxx      x         x        x   x          x     x               x   xxxxx   xxxxxx      x                    ",
   "    x x      x         x       xx o xx         x     x               x     o     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!!xx     xx!!!!!!!!xx    x!!!!!!!!!!     x     =     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxx     x!!!!!!!xx!     xxxxxxxxxxxxx xx  o o  xx                    ",
   "!!!!x x!!!!!!x         x!!!!!x    o                 xx!!!!!!xx !                    xx     xx                     ",
   "!!!!x x!!!!!!x         x!!!!!x                     xx!!!!!!xx  !                     xxxxxxx                      ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxxxxxx!!!!!!xx   !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!xxxxxxxxx!!!!!!!!!!!!!!!!!!xx    !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!xx     !                                                  "],
  ["                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                        o                                                                     ",
   "                                                                                                              ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                       xxx                                                                    ",
   "                                       x x                 !!!        !!!  xxx                                ",
   "                                       x x                 !x!        !x!                                     ",
   "                                     xxx xxx                x          x                                      ",
   "                                      x   x                 x   oooo   x       xxx                            ",
   "                                      x   x                 x          x      x!!!x                           ",
   "                                      x   x                 xxxxxxxxxxxx       xxx                            ",
   "                                     xx   xx      x   x      x                                                ",
   "                                      x   xxxxxxxxx   xxxxxxxx              x x                               ",
   "                                      x   x           x                    x!!!x                              ",
   "                                      x   x           x                     xxx                               ",
   "                                     xx   xx          x                                                       ",
   "                                      x   x= = = =    x            xxx                                        ",
   "                                      x   x           x           x!!!x                                       ",
   "                                      x   x    = = = =x     o      xxx       xxx                              ",
   "                                     xx   xx          x                     x!!!x                             ",
   "                              o   o   x   x           x     x                xxv        xxx                   ",
   "                                      x   x           x              x                 x!!!x                  ",
   "                             xxx xxx xxx xxx     o o  x!!!!!!!!!!!!!!x                   vx                   ",
   "                             x xxx x x xxx x          x!!!!!!!!!!!!!!x                                        ",
   "                             x             x   xxxxxxxxxxxxxxxxxxxxxxx                                        ",
   "                             xx           xx                                         xxx                      ",
   "  xxx                         x     x     x                                         x!!!x                xxx  ",
   "  x x                         x    xxx    x                                          xxx                 x x  ",
   "  x                           x    xxx    xxxxxxx                        xxxxx                             x  ",
   "  x                           x           x                              x   x                             x  ",
   "  x                           xx          x                              x x x                             x  ",
   "  x                                       x       |xxxx|    |xxxx|     xxx xxx                             x  ",
   "  x                xxx             o o    x                              x         xxx                     x  ",
   "  x               xxxxx       xx          x                             xxx       x!!!x          x         x  ",
   "  x               oxxxo       x    xxx    x                             x x        xxx          xxx        x  ",
   "  x                xxx        xxxxxxxxxxxxx  x oo x    x oo x    x oo  xx xx                    xxx        x  ",
   "  x      @          x         x           x!!x    x!!!!x    x!!!!x    xx   xx                    x         x  ",
   "  xxxxxxxxxxxxxxxxxxxxxxxxxxxxx           xxxxxxxxxxxxxxxxxxxxxxxxxxxxx     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  ",
   "                                                                                                              ",
   "                                                                                                              "],
  ["                                                                                                  xxx x       ",
   "                                                                                                      x       ",
   "                                                                                                  xxxxx       ",
   "                                                                                                  x           ",
   "                                                                                                  x xxx       ",
   "                          o                                                                       x x x       ",
   "                                                                                             o o oxxx x       ",
   "                   xxx                                                                                x       ",
   "       !  o  !                                                xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx       ",
   "       x     x                                                x   x x   x x   x x   x x   x x   x x           ",
   "       x= o  x            x                                   xxx x xxx x xxx x xxx x xxx x xxx x xxxxx       ",
   "       x     x                                                  x x   x x   x x   x x   x x   x x     x       ",
   "       !  o  !            o                                  xxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxxxx       ",
   "                                                                                                              ",
   "          o              xxx                              xx                                                  ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                      xx                                                      ",
   "                   xxx         xxx                                                                            ",
   "                                                                                                              ",
   "                          o                                                     x      x                      ",
   "                                                          xx     xx                                           ",
   "             xxx         xxx         xxx                                 x                  x                 ",
   "                                                                                                              ",
   "                                                                 ||                                           ",
   "  xxxxxxxxxxx                                                                                                 ",
   "  x         x o xxxxxxxxx o xxxxxxxxx o xx                                                x                   ",
   "  x         x   x       x   x       x   x                 ||                  x     x                         ",
   "  x  @      xxxxx   o   xxxxx   o   xxxxx                                                                     ",
   "  xxxxxxx                                     xxxxx       xx     xx     xxx                                   ",
   "        x=                  =                =x   x                     xxx                                   ",
   "        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   x!!!!!!!!!!!!!!!!!!!!!xxx!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
   "                                                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
   "                                                                                                              "]
];
// 一个设置公共的位置信息
function Vector(x,y){
    this.x = x
    this.y = y
}
// 标刻
var scale = 15
// 改变公共位置1
Vector.prototype.plus = function(other){
    return new Vector(this.x + other.x, this.y + other.y)
}
// 改变公共位置2
Vector.prototype.times = function(scale){
    return new Vector(this.x * scale,this.y * scale)
}
// 符号介绍
var actorchars =  {
    // 玩家符号，调用Player
	"@": Player,
    // 金币符号，调用Coin
	"o": Coin,
    // 左右移动的方块符号，调用Lava
	"=": Lava,
    // 上下移动的方块符号，调用Lava
	"|": Lava,
    // 从天上掉下来的方块符号，调用Lava
	"v": Lava
};
// 设置玩家位置
function Player(pos){
    // 当前复活点的位置
    this.pos = pos.plus(new Vector(0,-.5))
    // 人物大小宽0.5，长1
    this.size = new Vector(.5,1)
    // 速度
    this.speed = new Vector(0,0)
}
Player.prototype.type = "player"
// 玩家移动速度
var playerXSpeed = 10;
// 玩家水平移动函数
Player.prototype.moveX = function(step, level, keys) {
    // 在原地的坐标
    this.speed.x = 0;
    // 当键盘按下左键的时候
    if (keys.left) this.speed.x -= playerXSpeed;
    // 当键盘按下右键的时候
    if (keys.right) this.speed.x += playerXSpeed;
    // 每一步的x的数值
    var motion = new Vector(this.speed.x * step, 0);
    // 更新新的x坐标
    var newPos = this.pos.plus(motion);
    // 查看是否接触到岩浆或者墙壁
    var obstacle = level.obstacleAt(newPos, this.size);
    // 如果有碰到社么东西
    if (obstacle){
        level.playerTouched(obstacle);
    }
    else
        this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
// 玩家移动y轴
Player.prototype.moveY = function(step, level, keys) {
    // 每次调高的高度
    this.speed.y += step * gravity;
    // 产生坐标
    var motion = new Vector(0, this.speed.y * step);
    // 更改新坐标
    var newPos = this.pos.plus(motion);
    // 查看人物是否接触到社么
    var obstacle = level.obstacleAt(newPos, this.size);
    if (obstacle) {
        level.playerTouched(obstacle);
        if (keys.up && this.speed.y > 0)
        this.speed.y = -jumpSpeed;
        else
        this.speed.y = 0;
    } else {
        this.pos = newPos;
    }
};
// 人物动作
Player.prototype.act = function(step, level, keys) {
    this.moveX(step, level, keys);
    this.moveY(step, level, keys);
    // 调用是否吃到了金币
    var otherActor = level.actorAt(this);
    if (otherActor){
        level.playerTouched(otherActor.type, otherActor);
    }
    // 当失败的时候
    if (level.status == "lost") {
        this.pos.y += step;
        this.size.y -= step;
    }
};

// 设置岩浆
function Lava(pos, ch) {
	this.pos = pos;
	this.size = new Vector(1, 1);
	if (ch === "=")
		this.speed = new Vector(2, 0);
	else if (ch === '|')
		this.speed = new Vector(0, 2);
	else if (ch === 'v'){
		this.speed = new Vector(0, 3); 		   
		this.repeatPos = pos;
	}
}
Lava.prototype.type = "lava";
// 岩浆的动作
Lava.prototype.act = function(step, level) {
    // 新坐标
    var newPos = this.pos.plus(this.speed.times(step));
    // 查看是否接触到岩浆
    if (!level.obstacleAt(newPos, this.size))
        this.pos = newPos;
    else if (this.repeatPos)
        this.pos = this.repeatPos;
    else
        this.speed = this.speed.times(-1);
};

// 设置硬币
function Coin(pos) {
	this.basePos = this.pos = pos;
	this.size = new Vector(.6, .6);
	// take a look back
	this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";
// 金币摇晃的速度，摇晃的距离
var wobbleSpeed = 8, wobbleDist = 0.07;
// 金币的动作
Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};

// 难度设置
// plan是地图的样子，占几行几列
function Level(plan) {
    // 画布宽度
	this.width = plan[0].length;
    // 画布高度
	this.height = plan.length;
    // 网格
	this.grid = [];
    // 角色
	this.actors = [];
	// 遍历高度
	for (var y = 0; y < this.height; y++) {
        // 每一行画布的符号
		var line = plan[y],  gridLine = [];
		for (var x = 0; x < this.width; x++) {
            // 每一行每一个的符号
			var ch = line[x], fieldType = null;
            // 获取每一个符号应该调用社么函数
			var Actor = actorchars[ch];
            // 根据符号调用函数
			if (Actor)
                // 根据符号调用各个函数
				this.actors.push(new Actor(new Vector(x, y), ch));
			else if (ch === "x")
				fieldType = "wall";
			else if (ch === "!")
				fieldType = "lava";
			else if (ch === "|")
				fieldType = "lava";
			else if (ch === "=")
				fieldType = "lava";
			else if (ch === "v"){
				fieldType = "lava";
			}
            // 把每一行所有的格子都以fieldType展示，并且加入gridLine
			gridLine.push(fieldType)
		}
        // 把画布所有的格子以每一行的形式集中到grid
		this.grid.push(gridLine);
	}
    // 筛选出type === player的信息
	this.player = this.actors.filter(function(actor) {
		return actor.type === "player"
	})[0];	
    // 游戏状态
	this.status = this.finishDelay = null;
}
// 查看关卡是否结束
Level.prototype.isFinished = function() {
  return this.status != null && this.finishDelay < 0;
};
// 关卡的其他标准
Level.prototype.obstacleAt = function(pos, size) {
    // 人物开始的x坐标
    var xStart = Math.floor(pos.x);
    // 人物移动后的x坐标
    var xEnd = Math.ceil(pos.x + size.x);
    // 人物开始的y坐标
    var yStart = Math.floor(pos.y);
    // 人物移动后的y坐标
    var yEnd = Math.ceil(pos.y + size.y);
    // 人物碰到墙壁则return wall
    if (xStart < 0 || xEnd > this.width || yStart < 0)
        return "wall";
    // 人物高出地图则return lava
    if (yEnd > this.height)
        return "lava";
    for (var y = yStart; y < yEnd; y++) {
        for (var x = xStart; x < xEnd; x++) {
        var fieldType = this.grid[y][x];
        if (fieldType) return fieldType;
        }
    }
};
// 是否吃到金币
Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};

var maxStep = 0.05;
// 关卡动画
Level.prototype.animate = function(step, keys) {
  if (this.status != null)
    this.finishDelay -= step;

  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};
// 在关卡中人物接触到的东西
Level.prototype.playerTouched = function(type, actor) {
  if (type == "lava" && this.status == null) {
        this.status = "lost";
        this.finishDelay = 1;
        // 如果是coin，则遍历出所有coin的变量
    } else if (type == "coin") {
        this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    // 查看是否吃到了所有的金币
    if (!this.actors.some(function(actor) {
      return actor.type == "coin";
    })) {
        // 如果全吃到则将进入下一关
      this.status = "won";
      this.finishDelay = 1;
    }
  }
};

// 创建html元素
function element(name, className) {
	var elem = document.createElement(name);
	if(className) elem.className = className;
	return elem;
}

// 将某个元素放入主div，level是关卡
function DOMDisplay(parent, level) {
    // 创建一个主div元素，class命名为game
	this.wrap = parent.appendChild(element("div", "game"));
    // 关卡的难度的详细内容
	this.level = level;
	// 在主div元素中加入table元素
	this.wrap.appendChild(this.drawBackground());
    // 任务图层为空
	this.actorLayer = null;
	this.drawFrame();
}
// 创建背景样式和元素
DOMDisplay.prototype.drawBackground = function() {
    // element("元素名","class的命名")
	var table = element("table", "background");
    // table的样式根据关卡的长宽来定
	table.style.width = this.level.width * scale + "px";
	table.style.height = this.level.height * scale + "px";
    // 遍历关卡的网格
	this.level.grid.forEach(function(row) {
        // 在table中加入tr元素
        var rowElement = table.appendChild(element("tr"));
        // 在tr中更改样式
		rowElement.style.height = scale + "px";
        // 在tr中加入td元素，并且增加class的命名,type看情况而定
		row.forEach(function(type) {
			rowElement.appendChild(element("td", type));
		});
	});
	return table;
};
// 画人物
DOMDisplay.prototype.drawActors = function() {
    // 创建一个第一视角的div元素
	var wrap = element("div");
    // 不同的关卡创建不同的人物位置
	this.level.actors.forEach(function(actor) {
        // 在第一视角的div中添加另一个div，并且class命名为actor player
		var rect = wrap.appendChild(element("div", "actor " + actor.type));
        // 人物的长宽高
		rect.style.width = actor.size.x * scale + "px";
		rect.style.height = actor.size.y * scale + "px";
		rect.style.left = actor.pos.x * scale + "px";
		rect.style.top = actor.pos.y * scale + "px";
	});
	return wrap;
}
// 人物图层的视角情况
DOMDisplay.prototype.drawFrame = function() {
    // 查看是否有人物图层
	if (this.actorLayer)
        // 如果有则直接移除
		this.wrap.removeChild(this.actorLayer);
    // 没有则添加人物图层到主div中
	this.actorLayer = this.wrap.appendChild(this.drawActors());
    // 给主div添加class命名为game，如果关卡状态有改变，则额外添加class
	this.wrap.className = "game " + (this.level.status || "");
    // 调用锁定人物第一视角函数
	this.scrollPlayerIntoView();
};
// 锁定人物的第一视角
DOMDisplay.prototype.scrollPlayerIntoView = function() {
    // 第一视角的宽
    var width = this.wrap.clientWidth;
    // 第一视角的高
    var height = this.wrap.clientHeight;
    // 偏移的尺寸
    var margin = width / 3;

    // 锁定后人物向左右，上下移动的量
    var left = this.wrap.scrollLeft,
        right = left + width;
    var top = this.wrap.scrollTop, 
        bottom = top + height;
    // 每个关卡的玩家
    var player = this.level.player;
    // 获取人物的中心点
    var center = player.pos.plus(player.size.times(0.5))
                    .times(scale);
    // 如果人物的中心点 < left加上 指定的偏移尺寸则偏移指定尺寸
    if (center.x < left + margin)
        this.wrap.scrollLeft = center.x - margin;
    else if (center.x > right - margin)
        this.wrap.scrollLeft = center.x + margin - width;
    if (center.y < top + margin)
        this.wrap.scrollTop = center.y - margin;
    else if (center.y > bottom - margin)
        this.wrap.scrollTop = center.y + margin - height;
};
// 移除所有的人物角色和金币
DOMDisplay.prototype.clear = function() {
	this.wrap.parentNode.removeChild(this.wrap);
};

// 动画延迟
function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime != null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  // 但凡有一点点改变，都回调该函数frame
  requestAnimationFrame(frame);
}
// 变量
var arrowCodes = {37: "left", 38: "up", 39: "right"};
// 调用trackKeys
var arrows = trackKeys(arrowCodes);
// 创建键盘操控人物
function trackKeys(codes) {
    // 创建一个空的键盘操作数组
    var pressed = Object.create(null);
    function handler(event) {
        if (codes.hasOwnProperty(event.keyCode)) {
            var down = event.type == "keydown";
            // 当起跳后，落下应该false
            pressed[codes[event.keyCode]] = down;
            event.preventDefault();
        }
    }
    addEventListener("keydown", handler);
    addEventListener("keyup", handler);
    return pressed;
}
// runLevel(关卡难度的详细内容，加入样式的函数，查看当前状态)
function runLevel(level, Display, andThen) {
    // 调用DOMDisplay函数，把关卡内容加入到body,并且创建了一个变量
    var display = new Display(document.body, level);
    // 调用动画
    runAnimation(function(step) {
        // 调用Level函数中的animate方法,step为空
        level.animate(step, arrows);
        // 调用DOMDisplay函数中的drawFrame方法,step为空
        display.drawFrame(step);
        // 如果关卡结束，则数据清空，到下一关
        if (level.isFinished()) {
        display.clear();
        if (andThen)
            andThen(level.status);
        return false;
        }
    });
}
// 启动游戏函数（所有的地图，增加元素的函数）
function runGame(plans, Display) {
    // 启动关卡难度的函数
    function startLevel(n) {
        // runLevel(关卡难度的详细内容，加入样式的函数，查看当前状态)
        runLevel(new Level(plans[n]), Display, function(status) {
            if (status == "lost")
                startLevel(n);
            else if (n < plans.length - 1)
                startLevel(n + 1);
            else
                alert("You win!");
        });
    }
    startLevel(0);
}
// 启动游戏
// runGame(所有的地图,加入样式和元素的函数)
runGame(LEVELS, DOMDisplay);
</script>